1   /*
2    * Copyright (C) 2008 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect;
18  
19  import static com.google.common.base.Preconditions.checkArgument;
20  import static com.google.common.base.Preconditions.checkNotNull;
21  
22  import com.google.common.annotations.Beta;
23  import com.google.common.annotations.GwtCompatible;
24  import com.google.common.base.Function;
25  import com.google.common.base.Supplier;
26  
27  import java.io.Serializable;
28  import java.util.Comparator;
29  import java.util.Iterator;
30  import java.util.Map;
31  import java.util.NoSuchElementException;
32  import java.util.Set;
33  import java.util.SortedMap;
34  import java.util.SortedSet;
35  import java.util.TreeMap;
36  
37  import javax.annotation.Nullable;
38  
39  /**
40   * Implementation of {@code Table} whose row keys and column keys are ordered
41   * by their natural ordering or by supplied comparators. When constructing a
42   * {@code TreeBasedTable}, you may provide comparators for the row keys and
43   * the column keys, or you may use natural ordering for both.
44   *
45   * <p>The {@link #rowKeySet} method returns a {@link SortedSet} and the {@link
46   * #rowMap} method returns a {@link SortedMap}, instead of the {@link Set} and
47   * {@link Map} specified by the {@link Table} interface.
48   *
49   * <p>The views returned by {@link #column}, {@link #columnKeySet()}, and {@link
50   * #columnMap()} have iterators that don't support {@code remove()}. Otherwise,
51   * all optional operations are supported. Null row keys, columns keys, and
52   * values are not supported.
53   *
54   * <p>Lookups by row key are often faster than lookups by column key, because
55   * the data is stored in a {@code Map<R, Map<C, V>>}. A method call like {@code
56   * column(columnKey).get(rowKey)} still runs quickly, since the row key is
57   * provided. However, {@code column(columnKey).size()} takes longer, since an
58   * iteration across all row keys occurs.
59   *
60   * <p>Because a {@code TreeBasedTable} has unique sorted values for a given
61   * row, both {@code row(rowKey)} and {@code rowMap().get(rowKey)} are {@link
62   * SortedMap} instances, instead of the {@link Map} specified in the {@link
63   * Table} interface.
64   *
65   * <p>Note that this implementation is not synchronized. If multiple threads
66   * access this table concurrently and one of the threads modifies the table, it
67   * must be synchronized externally.
68   *
69   * <p>See the Guava User Guide article on <a href=
70   * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table">
71   * {@code Table}</a>.
72   *
73   * @author Jared Levy
74   * @author Louis Wasserman
75   * @since 7.0
76   */
77  @GwtCompatible(serializable = true)
78  @Beta
79  public class TreeBasedTable<R, C, V> extends StandardRowSortedTable<R, C, V> {
80    private final Comparator<? super C> columnComparator;
81  
82    private static class Factory<C, V>
83        implements Supplier<TreeMap<C, V>>, Serializable {
84      final Comparator<? super C> comparator;
85      Factory(Comparator<? super C> comparator) {
86        this.comparator = comparator;
87      }
88      @Override
89      public TreeMap<C, V> get() {
90        return new TreeMap<C, V>(comparator);
91      }
92      private static final long serialVersionUID = 0;
93    }
94  
95    /**
96     * Creates an empty {@code TreeBasedTable} that uses the natural orderings
97     * of both row and column keys.
98     *
99     * <p>The method signature specifies {@code R extends Comparable} with a raw
100    * {@link Comparable}, instead of {@code R extends Comparable<? super R>},
101    * and the same for {@code C}. That's necessary to support classes defined
102    * without generics.
103    */
104   public static <R extends Comparable, C extends Comparable, V>
105       TreeBasedTable<R, C, V> create() {
106     return new TreeBasedTable<R, C, V>(Ordering.natural(),
107         Ordering.natural());
108   }
109 
110   /**
111    * Creates an empty {@code TreeBasedTable} that is ordered by the specified
112    * comparators.
113    *
114    * @param rowComparator the comparator that orders the row keys
115    * @param columnComparator the comparator that orders the column keys
116    */
117   public static <R, C, V> TreeBasedTable<R, C, V> create(
118       Comparator<? super R> rowComparator,
119       Comparator<? super C> columnComparator) {
120     checkNotNull(rowComparator);
121     checkNotNull(columnComparator);
122     return new TreeBasedTable<R, C, V>(rowComparator, columnComparator);
123   }
124 
125   /**
126    * Creates a {@code TreeBasedTable} with the same mappings and sort order
127    * as the specified {@code TreeBasedTable}.
128    */
129   public static <R, C, V> TreeBasedTable<R, C, V> create(
130       TreeBasedTable<R, C, ? extends V> table) {
131     TreeBasedTable<R, C, V> result
132         = new TreeBasedTable<R, C, V>(
133             table.rowComparator(), table.columnComparator());
134     result.putAll(table);
135     return result;
136   }
137 
138   TreeBasedTable(Comparator<? super R> rowComparator,
139       Comparator<? super C> columnComparator) {
140     super(new TreeMap<R, Map<C, V>>(rowComparator),
141         new Factory<C, V>(columnComparator));
142     this.columnComparator = columnComparator;
143   }
144 
145   // TODO(jlevy): Move to StandardRowSortedTable?
146 
147   /**
148    * Returns the comparator that orders the rows. With natural ordering,
149    * {@link Ordering#natural()} is returned.
150    */
151   public Comparator<? super R> rowComparator() {
152     return rowKeySet().comparator();
153   }
154 
155   /**
156    * Returns the comparator that orders the columns. With natural ordering,
157    * {@link Ordering#natural()} is returned.
158    */
159   public Comparator<? super C> columnComparator() {
160     return columnComparator;
161   }
162 
163   // TODO(user): make column return a SortedMap
164 
165   /**
166    * {@inheritDoc}
167    *
168    * <p>Because a {@code TreeBasedTable} has unique sorted values for a given
169    * row, this method returns a {@link SortedMap}, instead of the {@link Map}
170    * specified in the {@link Table} interface.
171    * @since 10.0
172    *     (<a href="http://code.google.com/p/guava-libraries/wiki/Compatibility"
173    *     >mostly source-compatible</a> since 7.0)
174    */
175   @Override
176   public SortedMap<C, V> row(R rowKey) {
177     return new TreeRow(rowKey);
178   }
179 
180   private class TreeRow extends Row implements SortedMap<C, V> {
181     @Nullable final C lowerBound;
182     @Nullable final C upperBound;
183 
184     TreeRow(R rowKey) {
185       this(rowKey, null, null);
186     }
187 
188     TreeRow(R rowKey, @Nullable C lowerBound, @Nullable C upperBound) {
189       super(rowKey);
190       this.lowerBound = lowerBound;
191       this.upperBound = upperBound;
192       checkArgument(lowerBound == null || upperBound == null
193           || compare(lowerBound, upperBound) <= 0);
194     }
195 
196     @Override public SortedSet<C> keySet() {
197       return new Maps.SortedKeySet<C, V>(this);
198     }
199 
200     @Override public Comparator<? super C> comparator() {
201       return columnComparator();
202     }
203 
204     int compare(Object a, Object b) {
205       // pretend we can compare anything
206       @SuppressWarnings({"rawtypes", "unchecked"})
207       Comparator<Object> cmp = (Comparator) comparator();
208       return cmp.compare(a, b);
209     }
210 
211     boolean rangeContains(@Nullable Object o) {
212       return o != null && (lowerBound == null || compare(lowerBound, o) <= 0)
213           && (upperBound == null || compare(upperBound, o) > 0);
214     }
215 
216     @Override public SortedMap<C, V> subMap(C fromKey, C toKey) {
217       checkArgument(rangeContains(checkNotNull(fromKey))
218           && rangeContains(checkNotNull(toKey)));
219       return new TreeRow(rowKey, fromKey, toKey);
220     }
221 
222     @Override public SortedMap<C, V> headMap(C toKey) {
223       checkArgument(rangeContains(checkNotNull(toKey)));
224       return new TreeRow(rowKey, lowerBound, toKey);
225     }
226 
227     @Override public SortedMap<C, V> tailMap(C fromKey) {
228       checkArgument(rangeContains(checkNotNull(fromKey)));
229       return new TreeRow(rowKey, fromKey, upperBound);
230     }
231 
232     @Override public C firstKey() {
233       SortedMap<C, V> backing = backingRowMap();
234       if (backing == null) {
235         throw new NoSuchElementException();
236       }
237       return backingRowMap().firstKey();
238     }
239 
240     @Override public C lastKey() {
241       SortedMap<C, V> backing = backingRowMap();
242       if (backing == null) {
243         throw new NoSuchElementException();
244       }
245       return backingRowMap().lastKey();
246     }
247 
248     transient SortedMap<C, V> wholeRow;
249 
250     /*
251      * If the row was previously empty, we check if there's a new row here every
252      * time we're queried.
253      */
254     SortedMap<C, V> wholeRow() {
255       if (wholeRow == null
256           || (wholeRow.isEmpty() && backingMap.containsKey(rowKey))) {
257         wholeRow = (SortedMap<C, V>) backingMap.get(rowKey);
258       }
259       return wholeRow;
260     }
261 
262     @Override
263     SortedMap<C, V> backingRowMap() {
264       return (SortedMap<C, V>) super.backingRowMap();
265     }
266 
267     @Override
268     SortedMap<C, V> computeBackingRowMap() {
269       SortedMap<C, V> map = wholeRow();
270       if (map != null) {
271         if (lowerBound != null) {
272           map = map.tailMap(lowerBound);
273         }
274         if (upperBound != null) {
275           map = map.headMap(upperBound);
276         }
277         return map;
278       }
279       return null;
280     }
281 
282     @Override
283     void maintainEmptyInvariant() {
284       if (wholeRow() != null && wholeRow.isEmpty()) {
285         backingMap.remove(rowKey);
286         wholeRow = null;
287         backingRowMap = null;
288       }
289     }
290 
291     @Override public boolean containsKey(Object key) {
292       return rangeContains(key) && super.containsKey(key);
293     }
294 
295     @Override public V put(C key, V value) {
296       checkArgument(rangeContains(checkNotNull(key)));
297       return super.put(key, value);
298     }
299   }
300 
301   // rowKeySet() and rowMap() are defined here so they appear in the Javadoc.
302 
303   @Override public SortedSet<R> rowKeySet() {
304     return super.rowKeySet();
305   }
306 
307   @Override public SortedMap<R, Map<C, V>> rowMap() {
308     return super.rowMap();
309   }
310 
311   /**
312    * Overridden column iterator to return columns values in globally sorted
313    * order.
314    */
315   @Override
316   Iterator<C> createColumnKeyIterator() {
317     final Comparator<? super C> comparator = columnComparator();
318 
319     final Iterator<C> merged =
320         Iterators.mergeSorted(Iterables.transform(backingMap.values(),
321             new Function<Map<C, V>, Iterator<C>>() {
322               @Override
323               public Iterator<C> apply(Map<C, V> input) {
324                 return input.keySet().iterator();
325               }
326             }), comparator);
327 
328     return new AbstractIterator<C>() {
329       C lastValue;
330 
331       @Override
332       protected C computeNext() {
333         while (merged.hasNext()) {
334           C next = merged.next();
335           boolean duplicate =
336               lastValue != null && comparator.compare(next, lastValue) == 0;
337 
338           // Keep looping till we find a non-duplicate value.
339           if (!duplicate) {
340             lastValue = next;
341             return lastValue;
342           }
343         }
344 
345         lastValue = null; // clear reference to unused data
346         return endOfData();
347       }
348     };
349   }
350 
351   private static final long serialVersionUID = 0;
352 }